#include <algorithm>

#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <boost/lexical_cast.hpp>

#include "SCGI.h"
#include "Server.h"

using namespace scgi;

Clock::Clock() { }

void Clock::tick(int tick_count_)
{
	// because BOOST_DATE_TIME_HAS_REENTRANT_STD_FUNCTIONS not defined.
	mutex.lock();

	boost::posix_time::ptime
		  now_ = boost::posix_time::second_clock::universal_time()
		, epoch(boost::gregorian::date(1970,1,1));
	boost::posix_time::time_duration::sec_type sec = 
		(now_ - epoch).total_seconds();

	time = static_cast<long long>(sec);

	mutex.unlock();
}

Time Clock::now()
{
	mutex.lock();
	Time result = time;
	mutex.unlock();

	return result;
}

void Listener::accept()
{
	Listener::pointer listener = shared_from_this();
	Request::pointer request = Request::create(server);
	
	server.acceptor.async_accept(
		  request->socket
		, [=](boost::system::error_code const & error_)
		{
			listener->accept();
			request->start();
		});
}

Listener::pointer Listener::create(Server & server_)
{
	return Listener::pointer(new Listener(server_));
}

Server::Server(unsigned short port_, std::string root_path_)
	: work(service)
	, acceptor(service, tcp::endpoint(tcp::v4(), port_))
	, timer(service)
{
	session_table.open_database(root_path_ + "session.db");
	session_table.create_table();
	std::random_device rand;
	session_table.initialize(this, rand());

	start_tick(0);

	acceptor.set_option(boost::asio::socket_base::reuse_address(true));
	buffer_size = 1024;
}

void Server::start(int thread_count_, int listener_count_)
{
	for(; listener_count_; listener_count_--)
	{
		Listener::pointer listener = Listener::create(*this);
		listener->start();
	}

	for(; thread_count_; thread_count_--)
	{
		threads.create_thread(
			[&]()
			{
				for(;;)
				{
					try
					{
						service.run();
						break;
					}
					catch(int i_) { this->error(i_); }
					catch(...) { this->error(10000); }
				}
			});
	}
}

void Server::join() { threads.join_all(); }

void Server::error(int code_)
{
}

void Server::setNameFilter(std::string regex_, std::string replace_)
{
	boost::regex regex(regex_, boost::regex_constants::ECMAScript);
	filters.push_back(std::make_pair(regex, replace_));
}

void Server::start_tick(int tick_count_)
{
	tick_count_ = (24 * 60 * 60 < tick_count_) ? 0 : ++tick_count_;

	timer.expires_from_now(boost::posix_time::seconds(1));
	timer.async_wait(
		[=](boost::system::error_code const & error_)
		{
			this->clock.tick(tick_count_);
			this->session_table.tick(tick_count_);

			start_tick(tick_count_);
		});
}

void Server::assign(Factory::pointer factory_)
{
	factories[factory_->name()] = factory_;
}

Application::pointer Server::create(Request::pointer request_)
{
	Application::pointer result;

	std::string uri = request_->env["REQUEST_URI"];

	filters_type::const_iterator it1 = filters.begin();
	while(it1 != filters.end())
	{
		uri = boost::regex_replace(uri, it1->first, it1->second);
		++it1;
	}

	request_->path.parse(uri);
	std::string name = request_->path.file;
	size_t i1 = name.find('.', 0);
	if(i1 != std::string::npos) { name = name.substr(0, i1); }

	factories_type::const_iterator it4 = factories.find(name);
	if(it4 != factories.end()) { result = it4->second->create_application(); }

	return result;
}




